#!/bin/bash
#script base: 
#		OpenSSL 3.0.13 30 Jan 2024 (Library: OpenSSL 3.0.13 30 Jan 2024)
#		GNU bash, version 5.2.21(1)-release (x86_64-pc-linux-gnu)

#author: xieziqiang
#link: cheesejohn.xie@gmail.com



# 声明函数
declare -f test_dir
declare -f color 
declare -f test_file
declare -f ca_config
declare -f current_cert_config
declare -f main
set -u -e
# 是否同时部署CA? 1代表是，0代表否
CA_FLAG=1
# 证书存放路径
CERT_DIR="/usr/local/nginx/cert"
# CA相关变量
CA_DIR="/usr/local/nginx/CA" # CA文件存放路径
CA_CONF="${CA_DIR}/my_openssl.cnf"
CA_DAYS=3650 # CA证书有效期
CRL_DAYS=30


# 证书相关变量和数组
CN_DAYS=365
country_name="CN"
state_name="Beijing"
locality_name="Beijing"
organization_name="xie"
organizational_unit_name="xie"
ca_common_name="ca.home.xieziqiang"
# 以下指定需要生成的域名的证书，如果一个证书需要对应多个域名，则需要以,分隔，并且主域名需要在第一个
common_name=("www.home.xieziqiang,10.1.1.36" "mysql.home.xieziqiang,10.1.1.36" "tomcat.home.xieziqiang,10.1.1.36")


color() {
    local message="$1"
    local status="$2"
    local terminal_width
    terminal_width=$(tput cols)
    local middle_column=$(( terminal_width / 2))
    local SETCOLOR_SUCCESS="\033[1;32m"  # 绿色
    local SETCOLOR_FAILURE="\033[1;31m"  # 红色
    local SETCOLOR_WARNING="\033[1;33m"  # 黄色
    local SETCOLOR_NORMAL="\033[0m"      # 重置颜色
    printf '%s\n\033[%sG' "${message}"  "${middle_column}"
    # Print the status message
    printf "["
    case ${status} in
        success|0)
            echo -en  "${SETCOLOR_SUCCESS}  OK  "
            ;;
        failure|1)
            echo -en  "${SETCOLOR_FAILURE}FAILED"
            ;;
        *)
            echo -en "${SETCOLOR_WARNING}WARNING"
            ;;
    esac
    echo -e "${SETCOLOR_NORMAL}]"
}

test_dir () {
    local i;
    for i in "$@";do 
        if [ ! -d ${i} ];then 
            color "设置的目录${i}不存在！ 创建目录 ${i}" 3
            mkdir -p "${i}"
        fi
    done
}
test_file () {
    local i;
    for i in "$@";do 
        if [ ! -f "${i}" ];then 
            color "设置的文件${i}不存在！ 创建文件 ${i}" 3
            touch "${i}"
        fi
    done
}
ca_config () {
	test_dir ${CA_DIR} ${CERT_DIR} ${CA_DIR}/crl ${CA_DIR}/private ${CA_DIR}/newcerts
	test_file $CA_CONF ${CA_DIR}/index.txt ${CA_DIR}/serial
	echo 2024 > ${CA_DIR}/serial
	chmod 644 ${CA_DIR}/index.txt
	cat > $CA_CONF <<-EOF
	[ ca ]
	default_ca = CA_default
	
	[ CA_default ]
	dir = ${CA_DIR}
	certs = ${CERT_DIR}
	crl_dir= \$dir/crl
	new_certs_dir = \$dir/newcerts
	database = \$dir/index.txt
	certificate = \$dir/cacert.pem
	private_key = \$dir/private/cakey.pem
	serial = \$dir/serial
	crlnumber = \$dir/crlnumber
	crl = \$dir/crl.pem
	RANDFILE = \$dir/private/.rand
	default_days = ${CA_DAYS}
	default_crl_days = ${CRL_DAYS}
	default_md = sha256
	preserve = no
	policy = policy_match
	
	[ policy_match ]
	countryName = match
	stateOrProvinceName = match
	organizationName = match
	organizationalUnitName = optional
	commonName = supplied
	emailAddress = optional
	
	[ req ]
	default_bits       = 2048
	prompt             = no
	default_md         = sha256
	distinguished_name = dn
	x509_extensions = v3_ca # The extensions to add to the self-signed cert
	
	
	[ v3_ca ]
	subjectKeyIdentifier = hash
	authorityKeyIdentifier = keyid:always,issuer
	basicConstraints = critical, CA:true
	keyUsage = critical, digitalSignature, cRLSign, keyCertSign
	
	
	[ dn ]
	C  = ${country_name}
	ST = ${state_name}
	L  = ${locality_name}
	O  = ${organization_name}
	OU = ${organizational_unit_name}
	CN = ${ca_common_name}
	
	EOF
	color "已创建CA文件夹和文件" 0

	(umask 066; openssl req -new -nodes  -config ${CA_CONF} -keyout ${CA_DIR}/private/cakey.pem -out ${CA_DIR}/carequest.csr &> /dev/null)
	openssl req -x509  -new -nodes -days $CA_DAYS  -config ${CA_CONF} -key ${CA_DIR}/private/cakey.pem -out ${CA_DIR}/cacert.pem &> /dev/null
	echo -e "\E[1;32m **************************************CA配置完成！！！**************************************\E[0m"
	color "已在${CA_DIR}目录下生成
	CA私钥cakey.pem
	CA请求文件carequest.csr
	CA自签名证书cacert.pem" 0
}


current_cert_config () {
	CERT_NUM=${#common_name[@]}
	local i=0
	local j=0
	local name_arr_lenth=0
	for((i=0;i<CERT_NUM;i++));do
		IFS=" " read -r -a name_arr <<< "${common_name[i]//,/ }"
		name_arr_lenth=${#name_arr[@]}
		local cert_dirname="$CERT_DIR/${name_arr[0]}"
		if [ ! -d "$cert_dirname" ];then
			mkdir -p "$cert_dirname" && color "创建$cert_dirname文件夹" 3
		fi

		if [ -e "$cert_dirname/${name_arr[0]}_cert.pem" ];then
			color "$cert_dirname 目录下已存在证书文件！不再生成证书!" 1
			continue
		fi

		cat > "$cert_dirname/${name_arr[0]}.cnf" <<-EOF
		[ ca ]
		default_ca = CA_default
		
		[ CA_default ]
		dir = ${CA_DIR}
		certs = ${CERT_DIR}
		crl_dir= \$dir/crl
		new_certs_dir = \$dir/newcerts
		database = \$dir/index.txt
		certificate = \$dir/cacert.pem
		private_key = \$dir/private/cakey.pem
		serial = \$dir/serial
		crlnumber = \$dir/crlnumber
		crl = \$dir/crl.pem
		RANDFILE = \$dir/private/.rand
		default_days = ${CA_DAYS}
		default_crl_days = ${CRL_DAYS}
		default_md = sha256
		preserve = no
		policy = policy_match
		
		[ policy_match ]
		countryName = match
		stateOrProvinceName = match
		organizationName = match
		organizationalUnitName = optional
		commonName = supplied
		emailAddress = optional
		
		[ req ]
		default_bits       = 2048
		prompt             = no
		default_md         = sha256
		distinguished_name = dn
		req_extensions     = req_ext

		[ dn ]
		C  = ${country_name}
		ST = ${state_name}
		L  = ${locality_name}
		O  = ${organization_name}
		OU = ${organizational_unit_name}
		CN = ${name_arr[0]}		
		
		[ req_ext ]
		subjectAltName = @alt_names

		[ alt_names ]
		EOF
		cat >  "$cert_dirname/${name_arr[0]}.ext"<<-EOF
		authorityKeyIdentifier=keyid,issuer
		basicConstraints=CA:FALSE
		keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
		extendedKeyUsage = serverAuth,clientAuth
		subjectAltName = @alt_names

		[alt_names]	
		EOF
		for((j=0;j<name_arr_lenth;j++));do
			if [[ ${name_arr[j]}  =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]];then
				echo "IP.${j} = ${name_arr[j]}" >> "$cert_dirname/${name_arr[0]}.cnf"
				echo "IP.${j} = ${name_arr[j]}" >> "$cert_dirname/${name_arr[0]}.ext"	
			else
				echo "DNS.${j} = ${name_arr[j]}" >> "$cert_dirname/${name_arr[0]}.cnf"
				echo "DNS.${j} = ${name_arr[j]}" >> "$cert_dirname/${name_arr[0]}.ext"											
			fi
		done
		(umask 666;openssl req -new -nodes  -config "$cert_dirname/${name_arr[0]}.cnf" -keyout "${cert_dirname}/${name_arr[0]}_key.pem"  -out "${cert_dirname}/${name_arr[0]}_req.csr" &> /dev/null)

		openssl ca -batch -extfile "$cert_dirname/${name_arr[0]}.ext"   -days $CN_DAYS  -config "$cert_dirname/${name_arr[0]}.cnf" -in "${cert_dirname}/${name_arr[0]}_req.csr" \
		-key "${cert_dirname}/${name_arr[0]}_key.pem" -out "${cert_dirname}/${name_arr[0]}_cert.pem" &> /dev/null \
		|| { color "生成${name_arr[0]}证书失败，请检查index.txt是否已存在${name_arr[0]}证书！" 1;continue; }
            
		echo -e "\E[1;32m **************************************${name_arr[*]}证书配置完成！！！**************************************\E[0m"
		color "已在目录 ${cert_dirname} 下生成 
		私钥文件 ${name_arr[0]}_key.pem
		请求文件 ${name_arr[0]}_req.csr
		证书文件 ${name_arr[0]}_cert.pem" 0
	done	
}


main () {
	if [ $CA_FLAG -eq 1 ];then
		if [ -e "$CA_CONF" ];then
			color "因为，$CA_CONF 文件存在，所以跳过ca配置" 2
		else
			ca_config
		fi
	fi
	current_cert_config
}
main
